import { Directive, ElementRef, Input, OnInit } from '@angular/core';
import { AbstractControl, FormControl, NgModel, NG_VALIDATORS, Validator, ValidatorFn } from '@angular/forms';

import { ValidatorFactoryService } from '../../../core/services/validator-factory.service';

/**
 * Directive checks whether two input values are identical. 
 * Accpets the data binding with Array of the two field NgModel references. 
 * Array item order does not matter.
 */
@Directive({
    selector: '[valueNotMatch]',
    providers: [
        {
            provide: NG_VALIDATORS,
            useExisting: ValueNotMatchDirective,
            multi: true
        }
    ]
})
export class ValueNotMatchDirective implements OnInit, Validator {
    @Input() valueNotMatch: NgModel[];
    private referencedFieldNgModel: NgModel;
    private fieldNgModel: NgModel;

    constructor(
        private element: ElementRef,
        private validatorFactory: ValidatorFactoryService
    ) { }

    ngOnInit(): void {
        // If check the this.valueNotMatch without setTimeout, the second item in the array is missing some property values.
        window.setTimeout(() => {
            if (this.valueNotMatch && this.valueNotMatch.length === 2) {
                // Gets current element field NgModel from the binding array by name.
                this.fieldNgModel = this.valueNotMatch.find(p => p.name === this.element.nativeElement.name);

                // Gets reference field NgModel from the binding array by name difference.
                this.referencedFieldNgModel = this.valueNotMatch.find(p => p.name !== this.element.nativeElement.name);

                // Subscribes the valueChanges of the reference field NgModel. If the reference field value changes also triggers current field's validity change.
                this.referencedFieldNgModel.valueChanges.subscribe(() =>
                    this.validatorFactory.setValueNotMatchFieldModelError(this.referencedFieldNgModel, this.fieldNgModel)
                );
            }
        }, 0);
    }

    /**
     * Validates the input value. This function called by Angular automatically.
     * @param control Current html control. Passed in by Angular automatically.
     */
    validate(control: AbstractControl): { [key: string]: any } {
        if (this.referencedFieldNgModel && this.fieldNgModel) {
            return this.validatorFactory.getValueNotMatchValidatorDeprecated(this.referencedFieldNgModel, this.fieldNgModel)(control);
        } else {
            return null;
        }
    }
}